-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integration tests #148
Integration tests #148
Conversation
9d274d2
to
f502edd
Compare
39f7317
to
cc78cd5
Compare
cc78cd5
to
cdea9e1
Compare
53f6751
to
51e9ab5
Compare
Pull Request Test Coverage Report for Build 11481521970Details
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would appreciate if we could have a description about this PR. More comments in crates/marketplace/src/testing/integration.rs
would also be helpful for review. Thanks!
@dailinsubjam done, hopefully description + additional comments will be helpful. Please request more docs if not, there's no such thing as enough docs 🙂 |
{ | ||
type Event = Event<Types>; | ||
|
||
async fn handle_event(&mut self, (event, id): (Self::Event, usize)) -> anyhow::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know we could define names like (event, id)
here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do basically any patterns in function arguments!
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=712d93ebe3c92450a0d9a0483005c147
if id != 0 { | ||
return Ok(()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a comment about what id
is and why we can return Ok
if it's nonzero?
if let Err(e) = self | ||
.txn_history | ||
.iter() | ||
.map(|txn| txn.payload.number as i64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code here I feel like index
is probably a better field name than number
, to distinguish it from "count".
if id != 0 { | ||
return Ok(()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, can you add a comment about id
and why we return Ok
here?
// If the builder returns an error, we will re-try on the next view | ||
error => { | ||
tracing::warn!(?error, "Builder API error"); | ||
self.txn_queue.push_front(txn); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we push to txn_queue
if the submission fails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To try again next view, so that transactions we submit to the builder are numbered sequentially, otherwise we wouldn't be able to determine whether ordering failed because something's wrong with the builder or because one of transactions was rejected by the builder
n_nodes: usize, | ||
url: Url, | ||
config: Self::Config, | ||
_changes: HashMap<u64, BuilderChange>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this argument for future tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is required by the trait, in HotShot it is used to test how network behaves when some of the builders go offline / restart / start failing responses. As it mostly used to test proposer behavior rather than the builder's, I decided to ignore it here, although maybe we'll want to make use of it in some more complex tests
56b2610
to
1077625
Compare
fn start( | ||
self: Box<Self>, | ||
stream: Box< | ||
dyn futures::prelude::Stream<Item = hotshot::types::Event<Types>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like start()
in implementation of trait TestBuilderImplementation
is for starting [ProxyGlobalState
] APIs and initial [BuilderState
]'s event loop (as the comment said). Then what's this start()
for? What's the difference between run_builder_service()
and starting the event loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
run_builder_service
starts a task that takes a hotshot event stream and processes events from it (applies hooks and forwards to builder states)
The naming is rather non-intuitive I think, something to address during the upcoming refactor.
/// - `Random` submits a certain amount of random transactions every view | ||
/// - `Seeded` accepts a map of transactions to submit on each view | ||
/// - `Flood` continually submits transactions, re-trying if it encounters an error on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love this!
.iter() | ||
.map(|txn| txn.payload.index as i64) | ||
.try_fold(-1, |prev, next| { | ||
if prev + 1 == next { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we allow silent drop here? If so we might want to change to prev < next
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was writing this before we even figured out silent drop exists, but I'd leave it as is nonetheless because silent drop is extremely unlikely to happen in integration tests because all nodes behave and network is as good as it gets, so we won't trigger the silent drop accidentally. So I'd leave this check as-is because failure here would be very likely indication of another bug in builder rather than silent drop issue.
Then, as part of fixing the silent drop, we can specifically engineer a test where conditions for it happen in a reproducible manner.
This PR:
Adds an integration testing "framework" based on HotShot test harness.
Integration tests for builder are based on implementing
TestBuilderImplementation
, which allows running HotShot test harness with custom builder implementation. Additionally, this PR implements twoTestTask
s that can be injected into HotShot harness:TransactionGenerationTask
, which allows for more flexible transaction generation than what's provided in HotShot harnessBuilderValidationTask
, which applies custom validation logic to verify builder-related properties of the chain. Currently it only verifies transaction ordering and a transaction count threshold, but can be extended to verify other properties as needed, e.g. latency.This PR does not:
crates/marketplace/src/testing/integration.rs
Key places to review: